package com.ac.scum.log.task.utils.ftp;

import com.ac.common.core.exception.BaseException;
import com.ac.common.core.exception.config.ExceptionType;
import com.ac.scum.log.bean.ServerFTP;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;

@Slf4j
public class FTPUtil {
    private final ServerFTP serverFTP;
    private FTPClient ftpClient;
    private Session session;
    private ChannelSftp sftpChannel;
    private final static ConcurrentHashMap<String, Lock> lockMap = new ConcurrentHashMap<>();

    public FTPUtil(ServerFTP serverFTP) {
        this.serverFTP = serverFTP;
    }

    public void connect() throws IOException, JSchException {
        if (serverFTP.getSftpFlag()) {
            JSch jsch = new JSch();
            session = jsch.getSession(serverFTP.getUsername(), serverFTP.getIp(), serverFTP.getPort());
            session.setProxy(new ProxySOCKS5("18.179.200.158", 1080));
            session.setPassword(serverFTP.getPassword());
            session.setConfig("StrictHostKeyChecking", "no");
            session.setTimeout(20000);
            session.connect();
            Channel channel = session.openChannel("sftp");
            channel.connect();
            sftpChannel = (ChannelSftp) channel;
        } else {
            ftpClient = new FTPClient();

            ftpClient.setProxy(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("18.179.200.158", 1080)));
            ftpClient.setConnectTimeout(5000); // 设置连接超时时间为5秒
            ftpClient.connect(serverFTP.getIp(), serverFTP.getPort());
            ftpClient.login(serverFTP.getUsername(), serverFTP.getPassword());
            ftpClient.enterLocalPassiveMode();
            ftpClient.setDataTimeout(Duration.ofSeconds(30));
            ftpClient.setSoTimeout(30000);
        }
    }

    public void disconnect() {
        if (serverFTP.getSftpFlag()) {
            if (sftpChannel != null) {
                sftpChannel.disconnect();
            }
            if (session != null) {
                session.disconnect();
            }
        } else {
            if (ftpClient != null && ftpClient.isConnected()) {
                try {
                    ftpClient.logout();
                } catch (IOException e) {
                    log.error("FTP 登出失败 {}", e.getMessage());
                }
                try {
                    ftpClient.disconnect();
                } catch (IOException e) {
                    log.error("FTP 断开失败 {}", e.getMessage());
                }

            }
        }
    }

    public List<com.ac.scum.log.task.utils.ftp.FTPFile> listFiles(String remoteDir) throws IOException, SftpException {
        List<com.ac.scum.log.task.utils.ftp.FTPFile> ftpFileList = new ArrayList<>();
        if (serverFTP.getSftpFlag()) {
            sftpChannel.cd(remoteDir);
            @SuppressWarnings("unchecked") Vector<ChannelSftp.LsEntry> files = sftpChannel.ls(".");
            for (ChannelSftp.LsEntry file : files) {
                if (file.getFilename().equals(".") || file.getFilename().equals("..")) {
                    continue;
                }
                com.ac.scum.log.task.utils.ftp.FTPFile ftpFile = new com.ac.scum.log.task.utils.ftp.FTPFile();
                ftpFile.setSize(file.getAttrs().getSize());
                ftpFile.setName(file.getFilename());
                ftpFileList.add(ftpFile);
            }
        } else {
            FTPFile[] files = ftpClient.listFiles(remoteDir);
            for (FTPFile file : files) {
                com.ac.scum.log.task.utils.ftp.FTPFile ftpFile = new com.ac.scum.log.task.utils.ftp.FTPFile();
                ftpFile.setSize(file.getSize());
                ftpFile.setName(file.getName());
                ftpFileList.add(ftpFile);
            }
        }
        return ftpFileList;
    }

    public void downloadFile(String remoteFilePath, String localFilePath) throws IOException, SftpException {
        if (serverFTP.getSftpFlag()) {
            sftpChannel.get(remoteFilePath, localFilePath);
        } else {
            OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(localFilePath));
            ftpClient.retrieveFile(remoteFilePath, outputStream);
            outputStream.close();
        }
    }

    public void uploadFile(String localFilePath, String remoteFilePath) throws IOException, SftpException {
        if (serverFTP.getSftpFlag()) {
            sftpChannel.put(localFilePath, remoteFilePath);
        } else {
            InputStream inputStream = new FileInputStream(localFilePath);
            ftpClient.storeFile(remoteFilePath, inputStream);
            inputStream.close();
        }
    }

    public void uploadFile(InputStream inputStream, String remoteFilePath) throws IOException, SftpException {
        if (serverFTP.getSftpFlag()) {
            sftpChannel.put(inputStream, remoteFilePath);
        } else {
            ftpClient.storeFile(remoteFilePath, inputStream);
            inputStream.close();
        }
    }

    public synchronized void processFileInputStream(String remoteFilePath, InputStreamProcessor processor) throws IOException, SftpException, BaseException {
        InputStream inputStream;
        if (serverFTP.getSftpFlag()) {
            try {
                inputStream = sftpChannel.get(remoteFilePath);
            } catch (Exception e) {
                throw new BaseException(ExceptionType.System_Error.of(e.getMessage()), e);
            }
        } else {
            try {
                inputStream = ftpClient.retrieveFileStream(remoteFilePath);
            } catch (Exception e) {
                throw new BaseException(ExceptionType.System_Error.of(e.getMessage()), e);
            }
        }

        if (inputStream != null) {
            try {
                processor.process(inputStream);
            } catch (Exception e) {
                throw new BaseException(ExceptionType.System_Error.of(e.getMessage()), e);
            } finally {
                try {
                    inputStream.close();
                } catch (IOException ex) {
                    log.error("关闭输入流失败 {}", ex.getMessage());
                }
                if (!serverFTP.getSftpFlag()) {
                    ftpClient.completePendingCommand();
                }
            }
        }
    }

    @FunctionalInterface
    public interface InputStreamProcessor {
        void process(InputStream inputStream) throws IOException;
    }
}
